# -*- coding:utf-8 -*-
# 功能描述：根据解析得到的.CFG文件，解析数据文件（精简优化版）
import datetime as dt
import os
from collections import Counter
from pathlib import Path
import struct

import numpy as np
import pandas as pd

from parser.cfg_parser import Achannel_info, Dchannel_info, Cfg_file


class Data_file:
    """数据文件解析器 - 精简优化版"""

    def __init__(self, cfg_file):
        self.cfg = cfg_file
        self.fd = None
        self.ch_objects = None
        # 预处理的数据结构（优化版使用）
        self.timestamps = None
        self.analog_data = None
        self.digital_data = None
        self._data_parsed = False

    def check_file(self):
        """校验数据文件有效性并打开文件"""
        for ext in ['.DAT', '.dat']:
            filepath = self.cfg.datafile_name + ext
            if os.path.isfile(filepath):
                self.fd = open(filepath, 'rb')
                # 移除文件大小对比检查，直接返回True
                return True
        return False

    def parse_data(self, use_optimized=True):
        """解析数据文件"""
        if use_optimized:
            self._parse_data_optimized()
        else:
            self._parse_data_legacy()
        self._data_parsed = True

    def _parse_data_optimized(self):
        """优化版本：使用NumPy批量解析"""
        # 构造数据结构
        dtype = np.dtype([
            ('ts_hi', '<i4'),
            ('ts_lo', '<i4'),
            ('analog', f'<i2', (self.cfg.a_c,)),
            ('digital', 'u1', (self.cfg.bin_len,))
        ])

        # 读取并解析数据
        self.fd.seek(0)
        raw_data = self.fd.read()
        frame_len = dtype.itemsize
        n_frames = len(raw_data) // frame_len

        # 批量解析为结构化数组
        structured_array = np.frombuffer(raw_data[:n_frames * frame_len], dtype=dtype)

        # 预处理常用数据
        self.timestamps = structured_array['ts_lo'].astype(np.int64) * self.cfg.timemult
        self.analog_data = structured_array['analog']
        self.digital_data = structured_array['digital']


    def _parse_data_legacy(self):
        """传统版本：逐帧解析 - 完全按照原始代码逻辑"""
        # 按照原始代码的格式字符串
        fmt1 = '<2i'
        fmt2 = f'{self.cfg.a_c}h'
        fmt3 = f'{self.cfg.bin_len}c'
        fmt = fmt1 + fmt2 + fmt3

        self.fd.seek(0, 0)
        all_ch = []  # 对应原始代码的all_ch

        # 获取总采样点数
        total_samples = self.cfg.sample_rate_points[1][-1] if self.cfg.sample_rate_points[1] else 0

        for i in range(total_samples):
            bin_data = self.fd.read(self.cfg.data_len)
            if len(bin_data) < self.cfg.data_len:
                break
            tuple_data = struct.unpack(fmt, bin_data)
            all_ch.append(list(tuple_data))

        if all_ch:
            frames_array = np.array(all_ch)
            # 时间戳是第二列（索引1）
            self.timestamps = frames_array[:, 1].astype(np.int64) * self.cfg.timemult
            # 模拟通道数据从第3列开始
            self.analog_data = frames_array[:, 2:2 + self.cfg.a_c].astype(np.int16)
            # 数字通道数据在最后
            if self.cfg.bin_len > 0:
                # 需要特殊处理，因为原始数据是字符，需要转换为字节
                digital_chars = frames_array[:, 2 + self.cfg.a_c:]
                self.digital_data = np.zeros((len(all_ch), self.cfg.bin_len), dtype=np.uint8)
                for i, row in enumerate(digital_chars):
                    for j, char_bytes in enumerate(row):
                        if isinstance(char_bytes, bytes):
                            self.digital_data[i, j] = ord(char_bytes)
                        else:
                            self.digital_data[i, j] = int(char_bytes)

    def get_channels_data(self, channels=None, ensure_unique_names=True):
        """
        提取通道数据（整合原有功能）

        Args:
            channels: 通道ID列表或通道名列表，None表示全部
            ensure_unique_names: 是否确保列名唯一
        """
        if not self._data_parsed:
            raise RuntimeError("请先调用 parse_data() 解析数据")

        # 构建通道映射
        ch_info_list = self.cfg.Achannel_info + self.cfg.Dchannel_info
        id_to_info = {i + 1: info for i, info in enumerate(ch_info_list)}
        name_to_id = {info.cch_id: i + 1 for i, info in enumerate(ch_info_list)}

        # 确定目标通道
        if channels is None:
            target_ids = list(id_to_info.keys())
        else:
            target_ids = []
            for item in channels:
                if isinstance(item, int):
                    target_ids.append(item)
                elif isinstance(item, str):
                    if item not in name_to_id:
                        raise KeyError(f"未找到通道名 '{item}'")
                    target_ids.append(name_to_id[item])
                else:
                    raise TypeError("channels 只能包含 int 或 str")

        # 提取通道数据
        self.ch_objects = []
        for ch_id in target_ids:
            ch_obj = ChannelData(ch_id, id_to_info[ch_id])

            if isinstance(ch_obj.ch_info, Achannel_info):
                # 模拟量处理
                col_index = ch_id - 1
                raw_vals = self.analog_data[:, col_index].astype(np.float64)
                ch_obj.timestamps = self.timestamps.copy()
                ch_obj.values = ch_obj.ch_info.ma * raw_vals + ch_obj.ch_info.mb

            elif isinstance(ch_obj.ch_info, Dchannel_info):
                # 数字量处理
                d_idx = ch_id - self.cfg.a_c - 1
                byte_offset, bit_offset = divmod(d_idx, 8)
                byte_vals = self.digital_data[:, byte_offset]
                bit_vals = (byte_vals >> bit_offset) & 1

                ch_obj.timestamps = self.timestamps.copy()
                ch_obj.values = bit_vals

            self.ch_objects.append(ch_obj)

        # 处理重复列名
        if ensure_unique_names:
            self._ensure_unique_column_names()

    def _ensure_unique_column_names(self):
        """确保列名唯一性"""
        names = [ch.ch_info.cch_id for ch in self.ch_objects]
        name_counts = Counter(names)

        if any(count > 1 for count in name_counts.values()):
            print("检测到重复列名，正在处理...")
            name_usage = {}

            for ch in self.ch_objects:
                original_name = ch.ch_info.cch_id
                suffix = ''
                if name_counts[original_name] > 1:
                    # 添加相别辨识，便于后续识别
                    if ch.ch_info.munit == 'V':
                        suffix += 'U'
                    if ch.ch_info.munit == 'A':
                        suffix += 'I'
                    suffix += ch.ch_info.cph

                    count = name_usage.get(original_name, 0) + 1
                    name_usage[original_name] = count
                    ch.unique_name = f"{original_name}_{suffix}_{count}"
                    print(f"  '{original_name}' -> '{ch.unique_name}'")
                else:
                    ch.unique_name = original_name
        else:
            for ch in self.ch_objects:
                ch.unique_name = ch.ch_info.cch_id

    def export_data(self, path=None, save_csv=False, **kwargs):
        """
        导出数据（整合所有导出功能）

        Args:
            path: 保存路径
            save_csv: 是否保存CSV
            **kwargs: 其他参数（如 chunksize, float_format 等）
        """
        if not self.ch_objects:
            raise ValueError("没有通道数据，请先调用 get_channels_data()")

        # 计算时间索引
        abs_idx = self._compute_time_index()

        # 构建数据字典
        data_dict = {}
        for ch in self.ch_objects:
            col_name = getattr(ch, 'unique_name', ch.ch_info.cch_id)
            data_dict[col_name] = np.round(ch.values, 6)

        # 添加频率列
        # data_dict['Freq'] = self._generate_frequency_array(len(abs_idx))

        # 创建DataFrame
        df = pd.DataFrame(data_dict, index=abs_idx)
        df.index.name = '时间'

        # 保存CSV
        if save_csv:
            if path is None:
                path = f"csv/{dt.datetime.now().strftime('%Y%m%d%H%M%S')}.csv"

            Path(path).parent.mkdir(parents=True, exist_ok=True)

            # 导出参数
            export_params = {
                'sep': ',',
                'encoding': 'utf_8_sig',
                'index_label': '时间',
                'float_format': '%.6f'
            }
            export_params.update(kwargs)

            df.to_csv(path, **export_params)
            print(f"CSV 导出成功：{path}")

        return df

    def _compute_time_index(self):
        """计算绝对时间索引"""
        start_str = ' '.join(self.cfg.start_t)
        end_str = ' '.join(self.cfg.end_t)
        startdt = dt.datetime.strptime(start_str, "%d/%m/%Y %H:%M:%S.%f")

        base_timestamps = self.ch_objects[0].timestamps
        if base_timestamps[0] < 0:
            startdt = dt.datetime.strptime(end_str, "%d/%m/%Y %H:%M:%S.%f")
            print("该文件选取启动点为零点")
        else:
            print("该文件选取预录点为零点")

        rel_us = base_timestamps.astype('int64')
        return pd.to_datetime(startdt, utc=False) + pd.to_timedelta(rel_us, unit='us')

    def _generate_frequency_array(self, n_samples):
        """生成频率数组"""
        freqs, change_pts = self.cfg.sample_rate_points
        freq_arr = np.empty(n_samples, dtype='int64')

        start_idx = 0
        for freq, end_pt in zip(freqs, change_pts):
            freq_arr[start_idx:end_pt] = freq
            start_idx = end_pt

        return freq_arr

    def get_channel_stats(self, channel_name):
        """获取通道统计信息（新增实用功能）"""
        for ch in self.ch_objects:
            if getattr(ch, 'unique_name', ch.ch_info.cch_id) == channel_name:
                return {
                    'max': np.max(ch.values),
                    'min': np.min(ch.values),
                    'mean': np.mean(ch.values),
                    'std': np.std(ch.values),
                    'samples': len(ch.values)
                }
        raise KeyError(f"未找到通道: {channel_name}")

    def find_max_value(self, channel_name):
        """查找通道最大值及其时间（优化版本）"""
        for ch in self.ch_objects:
            if getattr(ch, 'unique_name', ch.ch_info.cch_id) == channel_name:
                max_idx = np.argmax(ch.values)
                return ch.values[max_idx], ch.timestamps[max_idx]
        raise KeyError(f"未找到通道: {channel_name}")

    def __enter__(self):
        """支持上下文管理器"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """自动关闭文件"""
        if self.fd:
            self.fd.close()


class ChannelData:
    """通道数据容器（简化版本）"""

    def __init__(self, ch_id, ch_info):
        self.ch_id = ch_id
        self.ch_info = ch_info
        self.timestamps = None
        self.values = None
        self.unique_name = None  # 用于处理重复列名


# 工具函数
def performance_monitor(func):
    """轻量级性能监控装饰器"""
    import time
    import functools

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start_time
        print(f"{func.__name__} 执行时间: {elapsed:.3f}秒")
        return result

    return wrapper


def batch_process_files(file_configs, output_dir="output"):
    """批量处理多个DAT文件（新增实用功能）"""
    results = []

    for i, cfg in enumerate(file_configs):
        print(f"处理文件 {i + 1}/{len(file_configs)}: {cfg.datafile_name}")

        try:
            with Data_file(cfg) as data_file:
                if data_file.check_file():
                    data_file.parse_data()
                    data_file.get_channels_data()

                    output_path = f"{output_dir}/file_{i + 1:03d}.csv"
                    df = data_file.export_data(path=output_path)
                    results.append({"file": cfg.datafile_name, "status": "success", "shape": df.shape})
                else:
                    results.append({"file": cfg.datafile_name, "status": "file_not_found"})

        except Exception as e:
            results.append({"file": cfg.datafile_name, "status": "error", "error": str(e)})

    return results


# 示例使用方法
def example_usage():
    """使用示例"""

    cfg = Cfg_file("example.cfg")

    # 基本用法
    with Data_file(cfg) as data_file:
        if data_file.check_file():
            # 解析数据
            data_file.parse_data(use_optimized=True)

            # 提取特定通道
            data_file.get_channels_data(channels=[1, 2, 3, "VOLTAGE"])

            # 导出数据
            df = data_file.export_data(save_csv=True, chunksize=10000)

            # 获取统计信息
            stats = data_file.get_channel_stats("VOLTAGE")
            print(f"电压统计: {stats}")

            # 查找最大值
            max_val, max_time = data_file.find_max_value("VOLTAGE")
            print(f"最大电压: {max_val} at {max_time}")


if __name__ == "__main__":
    # 性能测试
    @performance_monitor
    def test_performance():
        # 测试代码
        pass

    test_performance()